package com.opdar.plugins.mybatis.spring;

import com.opdar.plugins.mybatis.annotations.Namespace;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Map;
import java.util.Set;


/**
 * Created by shiju on 2016/12/9.
 */
public abstract class NamespaceScannerConfigurer implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;
    private String basePackage;
    private String tablePrefix = "t_";
    private String databaseName = "database";
    private ArrayList<Class<?>> mappers = new ArrayList<Class<?>>();

    public NamespaceScannerConfigurer() {
        this("");
    }

    public NamespaceScannerConfigurer(String basePackage) {
        this.basePackage = basePackage;
    }

    public ArrayList<Class<?>> getMappers() {
        return mappers;
    }

    public String getBasePackage() {
        return basePackage;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public String getTablePrefix() {
        return tablePrefix;
    }

    public void setTablePrefix(String tablePrefix) {
        this.tablePrefix = tablePrefix;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();

        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry) {
            @Override
            protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
                Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
                for (BeanDefinitionHolder holder : beanDefinitions) {
                    GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
                    if (definition instanceof ScannedGenericBeanDefinition) {
                        Map<String, Object> attributes = ((ScannedGenericBeanDefinition) definition).getMetadata().getAnnotationAttributes(Namespace.class.getName());
                        Class<?> mapperClz = (Class<?>) attributes.get("value");
                        if (!mappers.contains(mapperClz)) {
                            mappers.add(mapperClz);
                        }

                    }
                }
                return beanDefinitions;
            }

            @Override
            protected void registerDefaultFilters() {
                this.addIncludeFilter(new AnnotationTypeFilter(Namespace.class));
                super.registerDefaultFilters();
            }

            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata()
                        .hasAnnotation(Namespace.class.getName());
            }
        };
        scanner.setResourceLoader(this.applicationContext);
        scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
        onMapperInNamespace();
    }

    public abstract void onMapperInNamespace();

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("postProcessBeanFactory");
    }
}
